use std::cell::RefCell;
use std::rc::Rc;
use sdl2::render::{WindowCanvas, Texture};
use sdl2::rect::Rect as SdlRect;

pub trait Renderable {
    fn render(&self, renderer: &mut WindowCanvas, dest: Rectangle);
}

impl Rectangle {
    pub fn to_sdl(self) -> Option<SdlRect> {
        // Reject negative width and height
        assert!(self.w >= 0.0 && self.h >= 0.0);

        // SdlRect::new : `(i32, i32, u32, u32) -> Result<Option<SdlRect>>`
        Some(SdlRect::new(self.x as i32, self.y as i32, self.w as u32, self.h as u32))
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Rectangle {
    pub x: f64,
    pub y: f64,
    pub w: f64,
    pub h: f64,
}

impl Rectangle {
    pub fn new(x: f64, y: f64, w: f64, h: f64) -> Rectangle {
        Rectangle {
            x: x,
            y: y,
            w: w,
            h: h,
        }
    }
}

//#[derive(Clone)]
pub struct Sprite<'r> {
    pub tex: Rc<RefCell<Texture<'r>>>,
    src: Rectangle,
}

impl <'r> Sprite <'r> {
    /// Creates a new sprite by wrapping a `Texture`.
    pub fn new(texture: Texture) -> Sprite {
        let tex_query = texture.query();
        Sprite {
            tex: Rc::new(RefCell::new(texture)),
            src: Rectangle {
                w: tex_query.width as f64,
                h: tex_query.height as f64,
                x: 0.0,
                y: 0.0,
            }
        }
    }
}

impl Renderable for Sprite<'_> {
    fn render(&self, renderer: &mut WindowCanvas, dest: Rectangle) {
        renderer.copy(&mut self.tex.borrow_mut(), self.src.to_sdl(), dest.to_sdl()).unwrap()
    }
}

pub trait CopySprite<T> {
    fn copy_sprite(&mut self, sprite: &T, dest: Rectangle);
}

impl CopySprite<Sprite<'_>> for WindowCanvas {
    fn copy_sprite(&mut self, renderable: &Sprite, dest: Rectangle) {
        renderable.render(self, dest);
    }
}